// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once // 防止头文件重复包含

#include "GameplayTagContainer.h" // 游戏标签容器
#include "Math/IntRect.h" // 整数矩形
#include "ScalableFloat.h" // 可缩放浮点数
#include "WorldCollision.h" // 世界碰撞
#include "Input/LyraInputModifiers.h" // Lyra输入修改器
#include "DrawDebugHelpers.h" // 调试绘制辅助工具
#include "AimAssistInputModifier.generated.h" // 生成的头文件

class APlayerController; // 玩家控制器前向声明
class UInputAction; // 输入动作前向声明
class ULocalPlayer; // 本地玩家前向声明
class UShapeComponent; // 形状组件前向声明
class ULyraAimSensitivityData; // 瞄准灵敏度数据前向声明
class ULyraSettingsShared; // 共享设置前向声明

DECLARE_LOG_CATEGORY_EXTERN(LogAimAssist, Log, All); // 声明日志类别

/** 基于当前pawn常用视口数据的容器 */
struct FAimAssistOwnerViewData
{
    FAimAssistOwnerViewData() { ResetViewData(); } // 构造函数，调用重置数据方法

    /**
     * 基于当前玩家控制器更新"拥有者"信息。这会计算并存储用于确定哪些目标可见的信息，
     * 例如视图矩阵和当前旋转
     */
    void UpdateViewData(const APlayerController* PC);

    /** 将此数据集的所有属性重置为默认值 */
    void ResetViewData();

    /** 如果此拥有者结构体有有效的玩家控制器则返回true */
    bool IsDataValid() const { return PlayerController != nullptr && LocalPlayer != nullptr; } // 检查数据有效性

    FBox2D ProjectReticleToScreen(float ReticleWidth, float ReticleHeight, float ReticleDepth) const; // 将准星投影到屏幕
    FBox2D ProjectBoundsToScreen(const FBox& Bounds) const; // 将边界框投影到屏幕
    FBox2D ProjectShapeToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const; // 将形状投影到屏幕
    FBox2D ProjectBoxToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const; // 将盒子投影到屏幕
    FBox2D ProjectSphereToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const; // 将球体投影到屏幕
    FBox2D ProjectCapsuleToScreen(const FCollisionShape& Shape, const FVector& ShapeOrigin, const FTransform& WorldTransform) const; // 将胶囊体投影到屏幕

    /** 指向玩家控制器的指针，可用于计算检查可见目标所需的数据 */
    const APlayerController* PlayerController = nullptr; // 玩家控制器

    const ULocalPlayer* LocalPlayer = nullptr; // 本地玩家
    
    FMatrix ProjectionMatrix = FMatrix::Identity; // 投影矩阵
    
    FMatrix ViewProjectionMatrix = FMatrix::Identity; // 视图投影矩阵
    
    FIntRect ViewRect = FIntRect(0, 0, 0, 0); // 视图矩形
    
    FTransform ViewTransform = FTransform::Identity; // 视图变换
    
    FVector ViewForward = FVector::ZeroVector; // 视图前向向量
    
    // 玩家变换是actor的位置和控制器的旋转。
    FTransform PlayerTransform = FTransform::Identity; // 玩家变换
    
    FTransform PlayerInverseTransform = FTransform::Identity; // 玩家逆变换

    /** 当前帧与上一帧之间的移动增量 */
    FVector DeltaMovement = FVector::ZeroVector; // 增量移动

    /** 此拥有者所属队伍的ID。从ALyraPlayerState填充。如果拥有者没有玩家状态，则为INDEX_NONE */
    int32 TeamID = INDEX_NONE; // 队伍ID
};

/** 用于在帧之间缓存目标状态的容器 */
USTRUCT(BlueprintType) // 蓝图类型结构体
struct FLyraAimAssistTarget
{
    GENERATED_BODY() // 生成结构体主体

    FLyraAimAssistTarget() { ResetTarget(); } // 构造函数，调用重置目标方法

    bool IsTargetValid() const { return TargetShapeComponent.IsValid(); } // 检查目标是否有效

    void ResetTarget(); // 重置目标

    FRotator GetRotationFromMovement(const FAimAssistOwnerViewData& OwnerInfo) const; // 从移动获取旋转
    
    TWeakObjectPtr<UShapeComponent> TargetShapeComponent; // 目标形状组件（弱引用）
    
    FVector Location = FVector::ZeroVector; // 位置
    FVector DeltaMovement = FVector::ZeroVector; // 增量移动
    FBox2D ScreenBounds; // 屏幕边界

    float ViewDistance = 0.0f; // 视图距离
    float SortScore = 0.0f; // 排序分数

    float AssistTime = 0.0f; // 辅助时间
    float AssistWeight = 0.0f; // 辅助权重

    FTraceHandle VisibilityTraceHandle; // 可见性追踪句柄
    
    uint8 bIsVisible : 1; // 是否可见（位域）
    
    uint8 bUnderAssistInnerReticle : 1; // 是否在辅助内准星下（位域）
    
    uint8 bUnderAssistOuterReticle : 1; // 是否在辅助外准星下（位域）
    
protected:

    float CalculateRotationToTarget2D(float TargetX, float TargetY, float OffsetY) const; // 计算到目标的2D旋转
};

/** 过滤某些瞄准辅助目标的选项 */
USTRUCT(BlueprintType) // 蓝图类型结构体
struct FAimAssistFilter
{
    GENERATED_BODY() // 生成结构体主体

    FAimAssistFilter() // 构造函数，初始化默认值
        : bIncludeSameFriendlyTargets(false) // 包含同队目标
        , bExcludeInstigator(true) // 排除发起者
        , bExcludeAllAttachedToInstigator(false) // 排除附加到发起者的所有actor
        , bExcludeRequester(true) // 排除请求者
        , bExcludeAllAttachedToRequester(false) // 排除附加到请求者的所有actor
        , bTraceComplexCollision(false) // 追踪复杂碰撞
        , bExcludeDeadOrDying(true) // 排除死亡或濒死目标
    {}

    /** 如果为true，则包含任何目标，即使它们在我们的队伍中 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly) // 可在任何地方编辑，蓝图只读
    uint8 bIncludeSameFriendlyTargets : 1; // 包含同队目标（位域）
    
    /** 排除'RequestedBy->Instigator' Actor */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetSelection) // 可在任何地方编辑，蓝图只读，类别为目标选择
    uint8 bExcludeInstigator : 1; // 排除发起者（位域）
    
    /** 排除所有附加到'RequestedBy->Instigator' Actor的actor */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetSelection) // 可在任何地方编辑，蓝图只读，类别为目标选择
    uint32 bExcludeAllAttachedToInstigator : 1; // 排除附加到发起者的所有actor（位域）

    /** 排除'RequestedBy Actor */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetSelection) // 可在任何地方编辑，蓝图只读，类别为目标选择
    uint8 bExcludeRequester : 1; // 排除请求者（位域）
    
    /** 排除所有附加到'RequestedBy Actor的actor */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetSelection) // 可在任何地方编辑，蓝图只读，类别为目标选择
    uint8 bExcludeAllAttachedToRequester : 1; // 排除附加到请求者的所有actor（位域）
    
    /** 追踪复杂碰撞。 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetSelection) // 可在任何地方编辑，蓝图只读，类别为目标选择
    uint8 bTraceComplexCollision : 1; // 追踪复杂碰撞（位域）
    
    /** 排除所有死亡或濒死目标 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = TargetSelection) // 可在任何地方编辑，蓝图只读，类别为目标选择
    uint8 bExcludeDeadOrDying : 1; // 排除死亡或濒死目标（位域）

    /** 任何所属actor为此类型的目标将被排除。 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly) // 可在任何地方编辑，蓝图只读
    TSet<TObjectPtr<UClass>> ExcludedClasses; // 排除的类集合

    /** 具有任何这些标签的目标将被排除。 */
    FGameplayTagContainer ExclusionGameplayTags; // 排除的游戏标签容器

    /** 此范围外的任何目标将被排除 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly) // 可在任何地方编辑，蓝图只读
    double TargetRange = 10000.0; // 目标范围
};

/** 当有活动目标时瞄准辅助应如何表现的设置 */
USTRUCT(BlueprintType) // 蓝图类型结构体
struct FAimAssistSettings
{
    GENERATED_BODY() // 生成结构体主体

    FAimAssistSettings(); // 构造函数

    float GetTargetWeightForTime(float Time) const; // 获取时间对应的目标权重
    float GetTargetWeightMaxTime() const; // 获取目标权重最大时间
    
    // 瞄准辅助内准星在世界空间中的宽度。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat AssistInnerReticleWidth; // 辅助内准星宽度

    // 瞄准辅助内准星在世界空间中的高度。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat AssistInnerReticleHeight; // 辅助内准星高度

    // 瞄准辅助外准星在世界空间中的宽度。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat AssistOuterReticleWidth; // 辅助外准星宽度

    // 瞄准辅助外准星在世界空间中的高度。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat AssistOuterReticleHeight; // 辅助外准星高度

    // 瞄准准星在世界空间中的宽度。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat TargetingReticleWidth; // 瞄准准星宽度

    // 瞄准准星在世界空间中的高度。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat TargetingReticleHeight; // 瞄准准星高度

    // 从玩家摄像机用于收集潜在目标的范围。
    // 注意：使用视野进行缩放，以便通过屏幕大小限制目标。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat TargetRange; // 目标范围

    // 基于目标被瞄准的时间，目标具有多少权重。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    TObjectPtr<const UCurveFloat> TargetWeightCurve = nullptr; // 目标权重曲线

    // 当目标在内准星下时，目标和玩家移动对瞄准辅助拉力的贡献程度。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullInnerStrengthHip; // 腰射内拉力强度

    // 当目标在外准星下时，目标和玩家移动对瞄准辅助拉力的贡献程度。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullOuterStrengthHip; // 腰射外拉力强度

    // 当目标在内准星下时，目标和玩家移动对瞄准辅助拉力的贡献程度。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullInnerStrengthAds; // 瞄准内拉力强度

    // 当目标在外准星下时，目标和玩家移动对瞄准辅助拉力的贡献程度。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullOuterStrengthAds; // 瞄准外拉力强度

    // 用于增加拉力强度的指数插值速率。设置为'0'以禁用。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullLerpInRate; // 拉力淡入速率

    // 用于减少拉力强度的指数插值速率。设置为'0'以禁用。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullLerpOutRate; // 拉力淡出速率

    // 瞄准辅助拉力量的旋转速率上限。设置为'0'以禁用。
    // 注意：基于视野进行缩放，因此无论缩放如何感觉都相同。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat PullMaxRotationRate; // 最大旋转速率

    // 当目标在内准星下时，应用于期望转向速率的瞄准辅助减速量。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowInnerStrengthHip; // 腰射内减速强度

    // 当目标在外准星下时，应用于期望转向速率的瞄准辅助减速量。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowOuterStrengthHip; // 腰射外减速强度

    // 当目标在内准星下时，应用于期望转向速率的瞄准辅助减速量。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowInnerStrengthAds; // 瞄准内减速强度

    // 当目标在外准星下时，应用于期望转向速率的瞄准辅助减速量。(0 = 无, 1 = 最大)
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowOuterStrengthAds; // 瞄准外减速强度

    // 用于增加减速强度的指数插值速率。设置为'0'以禁用。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowLerpInRate; // 减速淡入速率

    // 用于减少减速强度的指数插值速率。设置为'0'以禁用。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowLerpOutRate; // 减速淡出速率

    // 瞄准辅助减速量的旋转速率下限。设置为'0'以禁用。
    // 注意：基于视野进行缩放，因此无论缩放如何感觉都相同。
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    FScalableFloat SlowMinRotationRate; // 最小旋转速率
    
    /** 在给定帧中可以考虑的最大目标数量。 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    int32 MaxNumberOfTargets = 6; // 最大目标数量

    /**  */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    float ReticleDepth = 3000.0f; // 准星深度

    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    float TargetScore_AssistWeight = 10.0f; // 目标分数_辅助权重

    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    float TargetScore_ViewDot = 50.0f; // 目标分数_视图点积

    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    float TargetScore_ViewDotOffset = 40.0f; // 目标分数_视图点积偏移

    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    float TargetScore_ViewDistance = 0.25f; // 目标分数_视图距离

    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    float StrengthScale = 1.0f; // 强度缩放

    /** 启用/禁用异步可见性追踪。 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bEnableAsyncVisibilityTrace : 1; // 启用异步可见性追踪（位域）

    /** 是否要求输入才能应用瞄准辅助 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bRequireInput : 1; // 要求输入（位域）

    /** 是否对瞄准辅助应用拉力 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bApplyPull : 1; // 应用拉力（位域）

    /** 是否基于移动输入应用平移拉力 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bApplyStrafePullScale : 1; // 应用平移拉力缩放（位域）
    
    /** 是否在瞄准辅助期间应用减速效果 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bApplySlowing : 1; // 应用减速（位域）

    /** 是否基于观察输入应用动态减速效果 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bUseDynamicSlow : 1; // 使用动态减速（位域）

    /** 观察速率是否应使用径向观察速率基于摇杆偏转在偏航和俯仰之间混合 */
    UPROPERTY(EditAnywhere) // 可在任何地方编辑
    uint8 bUseRadialLookRates : 1; // 使用径向观察速率（位域）
};

/**
 * 帮助游戏手柄玩家更好地瞄准的输入修改器。
 */
UCLASS() // 类声明
class UAimAssistInputModifier : public UInputModifier // 继承自UInputModifier
{
    GENERATED_BODY() // 生成类主体
    
public:
        
    UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category=Settings, Config) // 仅编辑实例，蓝图读写，类别设置，配置
    FAimAssistSettings Settings {}; // 瞄准辅助设置

    UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category=Settings, Config) // 仅编辑实例，蓝图读写，类别设置，配置
    FAimAssistFilter Filter {}; // 瞄准辅助过滤器

    /** 表示玩家实际移动的输入动作 */
    UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category=Settings) // 仅编辑实例，蓝图读写，类别设置
    TObjectPtr<const UInputAction> MoveInputAction = nullptr; // 移动输入动作
    
    /** 用于此灵敏度的瞄准类型 */
    UPROPERTY(EditInstanceOnly, BlueprintReadWrite, Category=Settings, Config) // 仅编辑实例，蓝图读写，类别设置，配置
    ELyraTargetingType TargetingType = ELyraTargetingType::Normal; // 瞄准类型

    /** 提供给我们用于灵敏度的浮点标量值的资产 */
    UPROPERTY(EditAnywhere, BlueprintReadOnly, meta=(AssetBundles="Client,Server")) // 可在任何地方编辑，蓝图只读，元数据指定资产包
    TObjectPtr<const ULyraAimSensitivityData> SensitivityLevelTable = nullptr; // 灵敏度等级表
    
protected:
    
    virtual FInputActionValue ModifyRaw_Implementation(const UEnhancedPlayerInput* PlayerInput, FInputActionValue CurrentValue, float DeltaTime) override; // 修改原始输入的实现

    /**
    * 交换目标缓存并确定当前哪些目标可见。
    * 更新每个目标的分数以确定
    * 应对每个目标应用多少拉力/减速效果
    */
    void UpdateTargetData(float DeltaTime); // 更新目标数据

    FRotator UpdateRotationalVelocity(APlayerController* PC, float DeltaTime, FVector CurrentLookInputValue, FVector CurrentMoveInputValue); // 更新旋转速度

    /** 计算给定目标的拉力和减速强度 */
    void CalculateTargetStrengths(const FLyraAimAssistTarget& Target, float& OutPullStrength, float& OutSlowStrength) const; // 计算目标强度

    FRotator GetLookRates(const FVector& LookInput); // 获取观察速率
    
    void SwapTargetCaches() { TargetCacheIndex ^= 1; } // 交换目标缓存
    const TArray<FLyraAimAssistTarget>& GetPreviousTargetCache() const    { return ((TargetCacheIndex == 0) ? TargetCache1 : TargetCache0); } // 获取上一帧目标缓存（常量）
    TArray<FLyraAimAssistTarget>& GetPreviousTargetCache()                { return ((TargetCacheIndex == 0) ? TargetCache1 : TargetCache0); } // 获取上一帧目标缓存

    const TArray<FLyraAimAssistTarget>& GetCurrentTargetCache() const    { return ((TargetCacheIndex == 0) ? TargetCache0 : TargetCache1); } // 获取当前帧目标缓存（常量）
    TArray<FLyraAimAssistTarget>& GetCurrentTargetCache()                { return ((TargetCacheIndex == 0) ? TargetCache0 : TargetCache1); } // 获取当前帧目标缓存

    bool HasAnyCurrentTargets() const { return !GetCurrentTargetCache().IsEmpty(); } // 是否有任何当前目标

    const float GetSensitivtyScalar(const ULyraSettingsShared* SharedSettings) const; // 获取灵敏度标量
    
    // 当前帧和上一帧目标的追踪
    UPROPERTY() // 属性声明
    TArray<FLyraAimAssistTarget> TargetCache0; // 目标缓存0

    UPROPERTY() // 属性声明
    TArray<FLyraAimAssistTarget> TargetCache1; // 目标缓存1

    /** 当前正在使用的目标缓存索引 */
    uint32 TargetCacheIndex; // 目标缓存索引

    FAimAssistOwnerViewData OwnerViewData; // 拥有者视图数据

    float LastPullStrength = 0.0f; // 上一帧拉力强度
    float LastSlowStrength = 0.0f; // 上一帧减速强度
    
#if ENABLE_DRAW_DEBUG // 如果启用绘制调试
    float LastLookRateYaw; // 上一帧偏航观察速率
    float LastLookRatePitch; // 上一帧俯仰观察速率

    FVector LastOutValue; // 上一帧输出值
    FVector LastBaselineValue; // 上一帧基准值

    // TODO: 移除此变量并将调试可视化移出此类 
    bool bRegisteredDebug = false; // 是否已注册调试

    void AimAssistDebugDraw(class UCanvas* Canvas, APlayerController* PC); // 瞄准辅助调试绘制
    FDelegateHandle DebugDrawHandle; // 调试绘制句柄
#endif
};